package com.yuncheng.shiro.authc;

import com.yuncheng.common.api.AuthenticationAPI;
import com.yuncheng.constant.CommonConstant;
import com.yuncheng.utils.I18nUtil;
import com.yuncheng.utils.ThreadContextHelper;
import com.yuncheng.vo.CurrentUserVo;
import com.yuncheng.rest.RestClient;
import com.yuncheng.vo.HttpResult;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import com.yuncheng.vo.LoginUser;
import com.yuncheng.shiro.utils.RequestUtils;
import com.yuncheng.utils.SpringContextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;

import javax.annotation.Resource;
import java.util.Map;

/**
 * 用户登录鉴权和获取用户授权
 */
public class ShiroRealmForRest extends AuthorizingRealm {

    private static final Logger log = LoggerFactory.getLogger(ShiroRealmForRest.class);

    @Resource
    private RestClient restClient;

    /**
     * 必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 权限信息认证(包括角色以及权限)是用户访问controller的时候才进行验证(redis存储的此处权限信息)
     * 触发检测用户权限时才会调用此方法，例如checkRole,checkPermission
     *
     * @param principals 身份信息
     * @return AuthorizationInfo 权限信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    /**
     * 用户信息认证是在用户进行登录的时候进行验证(不存redis)
     * 也就是说验证用户输入的账号和密码是否正确，错误抛出异常
     *
     * @param auth 用户登录的账号密码信息
     * @return 返回封装了用户信息的 AuthenticationInfo 实例
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        String token = (String) auth.getCredentials();
        if (token == null) {
            log.debug("————————身份认证失败——————————IP地址:  " + RequestUtils.getIpAddrByRequest(SpringContextUtils.getHttpServletRequest()));
            throw new AuthenticationException(I18nUtil.message("token.is.null"));
        }
        ThreadContextHelper.setToken(token);
        // 校验token有效性
        LoginUser loginUser = this.getLoginUser(token);

        CurrentUserVo currentUser = new CurrentUserVo();
        currentUser.setId(loginUser.getId());
//		currentUser.setCode(loginUser.getUsername());
        currentUser.setName(loginUser.getRealname());
        currentUser.setLoginName(loginUser.getUsername());
        currentUser.setDeptId(loginUser.getDeptId());
        currentUser.setDeptCode(loginUser.getDeptCode());
        currentUser.setDeptName(loginUser.getDeptName());
        currentUser.setOrgId(loginUser.getOrgId());
        currentUser.setOrgCode(loginUser.getOrgCode());
        currentUser.setOrgName(loginUser.getOrgName());
        Map<String, AuthenticationAPI> authenticationAPIMap = SpringContextUtils.getApplicationContext().getBeansOfType(AuthenticationAPI.class);
        authenticationAPIMap.forEach((k, v) -> v.setAuthUser(currentUser));
        ThreadContextHelper.setCurrentUser(currentUser);
        return new SimpleAuthenticationInfo(loginUser, token, getName());
    }

    private LoginUser getLoginUser(String token) throws AuthenticationException {
        String url = CommonConstant.PREFIX_SYSTEM + "/token/getLoginUser";
        HttpResult<LoginUser> httpResult = restClient.doGet(CommonConstant.SYSTEM_SERVICE_NAME, url, new ParameterizedTypeReference<HttpResult<LoginUser>>() {
        });
        if (httpResult.isSuccess()) {
            return httpResult.getResult();
        } else {
            throw new AuthenticationException(httpResult.getMessage());
        }
    }

    /**
     * 清除当前用户的权限认证缓存
     *
     * @param principals 权限信息
     */
    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }

}
